﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using UnitTests.Framework;
using System.IO;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace UnitTests.FrameworkTests
{
	/// <summary>
	/// Provides basic functionality for testing a ClientHandler
	/// </summary>
	/// <typeparam name="T">The ClientHandler type to test</typeparam>
	internal abstract class TestClientBase<T> : IDisposable where T : ClientHandlerBase, new()
	{
		private bool _disposed = false;
		private TcpClient _tcpClient = null;
		private TestServer<T> _testServer = null;
		private StreamReader _streamReader = null;
		private StreamWriter _streamWriter = null;

		/// <summary>
		/// Default constructor
		/// </summary>
		/// <param name="port">Tcp port to use</param>
		public TestClientBase(int port)
		{
			_testServer = new TestServer<T>(port);
			_tcpClient = new TcpClient();
			_tcpClient.Connect(new IPEndPoint(IPAddress.Loopback, port));
			Stream clientStream = _tcpClient.GetStream();
			_streamReader = new StreamReader(clientStream, Encoding.ASCII);
			_streamWriter = new StreamWriter(clientStream, Encoding.ASCII);
			_streamWriter.AutoFlush = true;

			Assert.IsTrue(VerifyWelcomeMessage(_streamReader.ReadLine()), "Incorrect welcome message received");
		}

		/// <summary>
		/// Gets the client handler that the server is currently using
		/// </summary>
		/// <returns>The client handler for this client</returns>
		public T FindClientHandler()
		{
			return _testServer.FindClientHandler(_tcpClient.Client.LocalEndPoint);
		}

		/// <summary>
		/// Sends a command to the server and checks that the reponse is the expected one
		/// </summary>
		/// <param name="command">Line to send to the server (excluding \r\n)</param>
		/// <param name="expectedResponse">The response that is expected from the server (case sensitive, excluding \r\n)</param>
		public void ProcessCommand(string command, string expectedResponse, string message)
		{
			Assert.AreEqual(WriteReadLine(command), expectedResponse, false, message);
		}

		/// <summary>
		/// When overriden in a derived class, checks that the server sent the correct welcome message
		/// </summary>
		/// <param name="message">Message that was received from the server</param>
		/// <returns>True if the message was valid, otherwise false</returns>
		protected abstract bool VerifyWelcomeMessage(string message);

		/// <summary>
		/// Writes a line, then reads a line
		/// </summary>
		/// <param name="outgoingData">Line of data to write (excluding \r\n)</param>
		/// <returns>Line of data read (excluding \r\n)</returns>
		protected string WriteReadLine(string outgoingData)
		{
			_streamWriter.WriteLine(outgoingData);
			return _streamReader.ReadLine();
		}

		/// <summary>
		/// Writes a line, then reads until a termination character is read on a line by itself
		/// </summary>
		/// <param name="outgoingData">Line to write (excluding the \r\n)</param>
		/// <param name="terminationCharacter">Termination character to look for</param>
		/// <returns>All of the data read until the termination character was received (excluding any trailing \r\n and the termination char itself)</returns>
		protected string WriteLineReadUntil(string outgoingData, char terminationCharacter)
		{
			StringBuilder readData = new StringBuilder();
			_streamWriter.WriteLine(outgoingData);
			bool addedLine = true;
			do
			{
				string lastLine = _streamReader.ReadLine();
				if ((lastLine.Length == 1) && (lastLine[0] == terminationCharacter))
				{
					addedLine = false;
				}
				else
				{
					readData.AppendLine(lastLine);
				}
			} while (addedLine);

			return readData.ToString();
		}

		#region IDisposable Members
		/// <summary>
		/// Disposes all managed and unmanaged resources
		/// </summary>
		public void Dispose()
		{
			Dispose(true);
			GC.SuppressFinalize(this);
		}

		/// <summary>
		/// Disposes all unmanaged resources and, optionally, the managed resources
		/// </summary>
		/// <param name="disposing">If true managed resources will be disposed</param>
		protected virtual void Dispose(bool disposing)
		{
			if (!_disposed)
			{
				if (disposing)
				{
					if (_streamWriter != null)
					{
						_streamWriter.Flush();
						_streamWriter.Close();
					}
					if (_streamReader != null)
						_streamReader.Close();
					if (_tcpClient != null)
						_tcpClient.Close();
					if (_testServer != null)
						_testServer.Dispose();
				}

				// Unmanaged Resources
			}

			_disposed = true;
		}
		#endregion
	}
}
